import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JComponent;

/**
 * Instances of this class have informations about fields of board.
 *  If, on current field of board there is an Moveable object then variable movObj is reference
 *  to that object. Variable 'type' is instance of Type (enum) and have information about type of this field.
 * @author mx109
 *
 */
public class Field extends JComponent implements Cloneable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 2616771090300399859L;
	public MoveableObject movObj;
	public Type type;
	private Position p;
	private static boolean mouseButton1Down = false;
	private static boolean mouseButton2Down = false;
	public Field(Field f){
		type = f.type;
		p = new Position(f.p);
	}
	/** @author wlis
	 *  Default images of things, which can appear on Field
	 */
	private static Image imgFloor, imgWall, imgBox, imgHole, imgPlayer1, imgPlayer2;
	static
	{
			try {
				imgFloor = ImageIO.read(new File("images/floor.jpg"));
				imgWall = ImageIO.read(new File("images/wall.jpg"));
				imgBox = ImageIO.read(new File("images/box.jpg"));
				imgHole = ImageIO.read(new File("images/hole.jpg"));
				imgPlayer1 = ImageIO.read(new File("images/player.jpg"));
				imgPlayer2 = ImageIO.read(new File("images/player2.jpg"));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
	/** Static method, used to change first player's image.
	 * @author crezax
	 */
	public static void setFirstAvatarImage(BufferedImage newImage){
		imgPlayer1 = newImage;
		try{
			ScaleJPG.scale(newImage, 40,40, "images/player.jpg");
		}
		catch(Exception e){}
	}
	/** Static method, used to change second player's image.
	 *  @author crezax
	 */
	public static void setSecondAvatarImage(BufferedImage newImage){
		imgPlayer2 = newImage;
		try{
			ScaleJPG.scale(newImage, 40,40, "images/player2.jpg");
		}
		catch(Exception e){}
	}
	/** Static method, used to change box's image.
	 *  @author crezax
	 */
	public static void setBoxImage(BufferedImage newImage){
		imgBox = newImage;
		try{
			ScaleJPG.scale(newImage, 40,40, "images/box.jpg");
		}
		catch(Exception e){}
	}
	/** Static method, used to change wall's image.
	 *  @author crezax
	 */
	public static void setWallImage(BufferedImage newImage){
		imgWall = newImage;
		try{
			ScaleJPG.scale(newImage, 40,40, "images/wall.jpg");
		}
		catch(Exception e){}
	}
	/** Static method, used to change hole's image.
	 *  @author crezax
	 */
	public static void setHoleImage(BufferedImage newImage){
		imgHole = newImage;
		try{
			ScaleJPG.scale(newImage, 40,40, "images/hole.jpg");
		}
		catch(Exception e){}
	}
	/** Static method, used to change floor's image.
	 *  @author crezax
	 */
	public static void setFloorImage(BufferedImage newImage){
		imgFloor = newImage;
		try{
			ScaleJPG.scale(newImage, 40,40, "images/floor.jpg");
		}
		catch(Exception e){}
	}
	/** Static method, used to restore default images.
	 *  @author crezax
	 */
	public static void setDefaultImages() {
		try {
			setFloorImage(ImageIO.read(new File("./images/floor_default.jpg")));
			setWallImage(ImageIO.read(new File("./images/wall_default.jpg")));
			setBoxImage(ImageIO.read(new File("./images/box_default.jpg")));
			setHoleImage(ImageIO.read(new File("./images/hole_default.jpg")));
			setFirstAvatarImage(ImageIO.read(new File("./images/player_default.jpg")));
			setSecondAvatarImage(ImageIO.read(new File("./images/player2_default.jpg")));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/** Constructor, that prepares empty Fields
	 *  @author wlis
	 */
	public Field()
	{
		type = Type.EMPTY;
		movObj = null;
		p = new Position();
		this.setPreferredSize(new Dimension(40, 40));
		this.setFocusable(false);
	}
	
	/** Equals method, that checks if 2 fields have the same type and object laying on them
	 *  @author Maciej Piekarz
	 */
	@Override
	public boolean equals(Object o){
		if (!(o instanceof Field)) return false;
		Field f = (Field) o;
		if (type != f.type) return false;
		if (!p.equals(f.p)) return false;
		if (movObj instanceof Player && f.movObj instanceof Box) return false;
		if (movObj instanceof Box && f.movObj instanceof Player) return false;
		return true;
	}
	
	/**
	 * return position of this field
	 * @return position of this field
	 */
	public Position getPosition(){
		return p;
	}
	
	/**
	 * set position of this field
	 * @param p new position of this field
	 */
	public void setPosition(Position p){
		this.p = p;
	}
	
	void setMovObj(MoveableObject movObj){this.movObj=movObj;}
	
	/** Used to draw a Field
	 *  @author wlis
	 */
	@Override
	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		int border = getWidth()/10;
		int width = getWidth();
		int height = getHeight();
		switch(type)
		{
		case WALL:
			g.drawImage(imgWall, 0, 0, width, height, null, null);
			break;
		case HOLE:
			g.drawImage(imgHole, 0, 0, width, height, null, null);
			break;
		default:
			g.drawImage(imgFloor, 0, 0, width, height, null, null);
			break;
		}
		if(movObj != null)
		{
			
			if(movObj.getClass() == Player.class)
			{
				Player p = (Player) movObj;
				if (p.getId() == 0) g.drawImage(imgPlayer1, border, border, width-2*border, height-2*border, null, null);
				else g.drawImage(imgPlayer2, border, border, width-2*border, height-2*border, null, null);
			}
			else if(movObj.getClass() == Box.class)
			{
				g.drawImage(imgBox, border, border, width-2*border, height-2*border, null, null);
			}
		}
	}
	
	/**
	 * Changes type of field to next state during board creation.
	 * @author wlis
	 */
	private void nextTypeState()
	{
		switch(type)
		{
			case EMPTY:
				type = Type.WALL;
				break;
			case WALL:
				type = Type.HOLE;
				break;
			case HOLE:
				type = Type.EMPTY;
		}
	}
	
	/**
	 * Changes movable object to next during board creation.
	 * @author wlis
	 */
	private void nextMovObjState()
	{
		if(movObj == null)
		{
			movObj = new Player(getPosition(), FieldClickedListener.board);
		}
		else if(movObj instanceof Player)
		{
			movObj = new Box(getPosition(), FieldClickedListener.board);
		}
		else movObj = null;
	}
	
	/** Function used to draw a Field
	 *  @author wlis
	 */
	static class FieldClickedListener implements MouseListener
	{
		Field field;
		static Board board;
		FieldClickedListener(Board board,Field field)
		{
			FieldClickedListener.board = board;
			this.field = field;
		}
		@Override
		public void mouseClicked(MouseEvent evt) {
			
		}

		@Override
		public void mouseEntered(MouseEvent arg0) {
			if(mouseButton1Down)
			{
				field.nextTypeState();
			}
			else if(mouseButton2Down)
			{
				field.nextMovObjState();
			}
			field.repaint();
		}

		@Override
		public void mouseExited(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mousePressed(MouseEvent evt) {
			if(evt.getButton() == MouseEvent.BUTTON1)
			{
				field.nextTypeState();
				mouseButton1Down = true;
			}
			else if(evt.getButton() == MouseEvent.BUTTON3)
			{
				field.nextMovObjState();
				mouseButton2Down = true;
			}
			field.repaint();
		}

		@Override
		public void mouseReleased(MouseEvent evt) {
			if(evt.getButton() == MouseEvent.BUTTON1)
			{
				mouseButton1Down = false;
			}
			else if(evt.getButton() == MouseEvent.BUTTON3)
			{
				mouseButton2Down = false;
			}
			
		}
	}

	
}
